package me.kuye.jkuye.render;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;

import me.kuye.jkuye.core.Const;
import me.kuye.jkuye.helper.PathHelper;
import me.kuye.jkuye.util.StringUtil;

public class FileRender extends Render {
	private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
	private File file;
	private static String fileDownloadPath;
	private static String webRootPath;
	private static ServletContext servletContext;

	public FileRender(File file) {
		this.file = file;
	}

	public FileRender(String fileName) {
		fileName = fileName.startsWith("/") ? webRootPath + fileName : fileDownloadPath + fileName;
		this.file = new File(fileName);
	}

	@Override
	public void render() {
		if (file == null || !file.isFile()) {
			RenderFactory.getInstance().getErrorRender(404).setContext(request, response).render();
			return;
		}
		response.setHeader("Accept-Ranges", "bytes");
		response.setHeader("Content-disposition", "attachment;filename" + encodeFileName(file.getName()));
		String contentType = servletContext.getMimeType(file.getName());
		response.setContentType(contentType != null ? contentType : DEFAULT_CONTENT_TYPE);
		if (StringUtil.isEmpty(request.getHeader("Range"))) {
			normalRender();
		} else {
			rangeRender();
		}
	}

	private void rangeRender() {
		Long[] range = { null, null };
		processRange(range);
		String contentLength = String.valueOf(range[1].longValue() - range[0].longValue() + 1);
		response.setHeader("Content-Length", contentLength);
		response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

		StringBuilder contentRange = new StringBuilder("bytes ").append(String.valueOf(range[0])).append("-")
				.append(String.valueOf(String.valueOf(range[1]))).append("/").append(String.valueOf(file.length()));

		response.setHeader("Content-Range", contentRange.toString());

		InputStream input = null;
		OutputStream out = null;
		try {
			long start = range[0];
			long end = range[1];
			input = new BufferedInputStream(new FileInputStream(file));
			if (input.skip(start) != start) {
				throw new RuntimeException("the skip error");
			}
			out = response.getOutputStream();
			byte[] buffer = new byte[1024];
			long position = start;
			for (int len; position < end && ((len = input.read(buffer)) != -1);) {
				if (position + len < end) {
					out.write(buffer, 0, len);
				} else {
					for (int i = 0; i < len && position <= len; i++) {
						out.write(buffer[i]);
						position++;
					}
				}
			}
			out.flush();
		} catch (IOException e) {
			if (isDevMode())
				throw new RenderException(e);
		} catch (Exception e) {
			throw new RenderException(e);
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private void processRange(Long[] range) {
		String rangeStr = request.getHeader("Range");
		int index = rangeStr.indexOf(",");
		if (index != -1) {
			rangeStr = rangeStr.substring(0, index);
		}
		rangeStr.replace("bytes=", "");

		String[] attr = rangeStr.split("-", 2);
		if (attr.length < 2) {
			throw new RuntimeException(" the range errore");
		}

		long fileLength = file.length();
		for (int i = 0; i < range.length; i++) {
			range[i] = Long.parseLong(attr[i].trim());
			if (range[i] >= fileLength) {
				range[i] = fileLength - 1;
			}
		}
		if (range[0] != null && range[1] == null) {
			range[1] = fileLength - 1;
		} else if (range[0] == null && range[1] != null) {
			range[0] = fileLength - range[1];
			range[1] = fileLength - 1;
		}

		if (range[0] == null || range[1] == null || range[0].longValue() > range[1].longValue()) {
			throw new RenderException(" the range can not be null");
		}
	}

	private void normalRender() {
		response.setHeader("Content-Length", String.valueOf(file.length()));

		InputStream input = null;
		OutputStream output = null;
		try {
			input = new BufferedInputStream(new FileInputStream(file));
			output = response.getOutputStream();
			byte[] buffer = new byte[1024];
			for (int len = -1; ((len = input.read(buffer)) != -1);) {
				output.write(buffer);
			}
			output.flush();
		} catch (IOException e) {
			if (isDevMode()) {
				throw new RenderException(e);
			}
		} catch (Exception e) {
			throw new RenderException(e);
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (output != null) {
				try {
					output.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private String encodeFileName(String name) {
		try {
			return new String(name.getBytes("GBK"), "ISO8859-1");
		} catch (UnsupportedEncodingException e) {
			return name;
		}
	}

	public static void init(String fileDownloadPath, ServletContext servletContext) {
		FileRender.fileDownloadPath = fileDownloadPath;
		FileRender.servletContext = servletContext;
		FileRender.webRootPath = PathHelper.getWebRootPath();
	}

}
